#include "OptionsMenu.h"
#include "Game.h"
#include "GameObject.h"
#include "Transformation.h"
#include "SimpleText.h"
#include "GLUtils.h"
#include "GLFont2D.h"
#include "MainMenu.h"
#include "Exception.h"
#include "Logger.h"

using namespace game_engine;
using namespace game_engine::components;
using namespace game_engine::utils;
using namespace game_engine::open_gl;
using namespace game_engine::gui;

///////////////////////////////////////////////////////////////////////////////
// Constructors and Destructors
///////////////////////////////////////////////////////////////////////////////

OptionsMenu::OptionsMenu(void)
    : Scene("OptionsMenu"), _btnEasy(NULL), _btnMedium(NULL), _btnHard(NULL),
    _btnLow(NULL), _btnModerate(NULL), _btnHigh(NULL),
    _btnOk(NULL), _btnCancel(NULL), _chkMusic(NULL), _chkFX(NULL)
{
    _difficultyGroup.clear();
    _graphDetailGroup.clear();
    _widgets.clear();
    _btnConnections.clear();
}

OptionsMenu::~OptionsMenu(void)
{
}

///////////////////////////////////////////////////////////////////////////////
// Other public methods
///////////////////////////////////////////////////////////////////////////////

const BOOL OptionsMenu::initialize(void)
{
    BOOL ok = true;
   
    try
    {
        // Create the objects
        createObjects();

        // Initialize all game objects
        initGameObjects();

        // This is where all connections to signals are defined
        boost::signals2::connection c;
        c = _btnEasy->connect(_btnEasy->signalClicked(), boost::bind(&OptionsMenu::onEasy, this, _1));
        _btnConnections.push_back(c);
        c = _btnMedium->connect(_btnMedium->signalClicked(), boost::bind(&OptionsMenu::onMedium, this, _1));
        _btnConnections.push_back(c);
        c = _btnHard->connect(_btnHard->signalClicked(), boost::bind(&OptionsMenu::onHard, this, _1));
        _btnConnections.push_back(c);

        c = _btnLow->connect(_btnLow->signalClicked(), boost::bind(&OptionsMenu::onLow, this, _1));
        _btnConnections.push_back(c);
        c = _btnModerate->connect(_btnModerate->signalClicked(), boost::bind(&OptionsMenu::onModerate, this, _1));
        _btnConnections.push_back(c);
        c = _btnHigh->connect(_btnHigh->signalClicked(), boost::bind(&OptionsMenu::onHigh, this, _1));
        _btnConnections.push_back(c);

        c = _chkMusic->connect(_chkMusic->signalToggled(), boost::bind(&OptionsMenu::onToggleMusic, this, _1));
        _btnConnections.push_back(c);
        c = _chkFX->connect(_chkFX->signalToggled(), boost::bind(&OptionsMenu::onToggleFX, this, _1));
        _btnConnections.push_back(c);

        c = _btnOk->connect(_btnOk->signalClicked(), boost::bind(&OptionsMenu::onOk, this, _1));
        _btnConnections.push_back(c);
        c = _btnCancel->connect(_btnCancel->signalClicked(), boost::bind(&OptionsMenu::onCancel, this, _1));
        _btnConnections.push_back(c);

        // Graphics initialization
        GLUtils::commonLevelInit();
        GLUtils::levelInit(resolution.getWidth(), resolution.getHeight()); 
    } catch (exception &e) {
        Logger::FATAL(GAME_LOG, string("Error during OptionsMenu initialization : ") + 
            e.what());
        return false;
    }

    nextScene = this;

    return ok;
}

const BOOL OptionsMenu::update(const FLOAT32 time)
{
    // Update the whole scene
    BOOL ok = true;
    try
    {
        updateGameObjects(time);
    } catch (exception &e)
    {
        string err("Error during scene update: ");
        err.append(e.what());
        Logger::ERR(GAME_LOG, err);
        return false;
    }

    return ok;
}

void OptionsMenu::finalize(void)
{
    try
    {
        // Finalize game objects
        finishGameObjects();

        // This is where all connections to signals are destroyed
        vector<boost::signals2::connection>::iterator it;
        for (it=_btnConnections.begin(); it<_btnConnections.end(); it++)
        {
            (*it).disconnect();
        }
    } catch (exception &e)
    {
        string err("Error during scene finalization: ");
        err.append(e.what());
        Logger::ERR(GAME_LOG, err);
        throw RuntimeException(err);
    }
}

///////////////////////////////////////////////////////////////////////////////
// Slots
///////////////////////////////////////////////////////////////////////////////

void OptionsMenu::onEasy(BOOL clicked)
{
    if (clicked)
    {
        setDifficulty(_btnEasy);
        _options.setDifficulty(EASY);
    }
}

void OptionsMenu::onMedium(BOOL clicked)
{
    if (clicked)
    {
        setDifficulty(_btnMedium);
        _options.setDifficulty(MEDIUM);
    }
}

void OptionsMenu::onHard(BOOL clicked)
{
    if (clicked)
    {
        setDifficulty(_btnHard);
        _options.setDifficulty(HARD);
    }
}

void OptionsMenu::onLow(BOOL clicked)
{
    if (clicked)
    {
        setGraphicDetail(_btnLow);
        _options.setGraphicDetail(LOW);
    }
}

void OptionsMenu::onModerate(BOOL clicked)
{
    if (clicked)
    {
        setGraphicDetail(_btnModerate);
        _options.setGraphicDetail(MODERATE);
    }
}

void OptionsMenu::onHigh(BOOL clicked)
{
    if (clicked)
    {
        setGraphicDetail(_btnHigh);
        _options.setGraphicDetail(HIGH);
    }
}

void OptionsMenu::onToggleMusic(BOOL toggled)
{
    _options.setMusicEnabled(toggled);
}

void OptionsMenu::onToggleFX(BOOL toggled)
{
    _options.setFxEnabled(toggled);
}

void OptionsMenu::onOk(BOOL clicked)
{
    if (clicked)
    {
        setMenuFocus(_btnOk);
        MainMenu *mainMenu = new MainMenu();
        mainMenu->setResolution(Size(512, 640));
        nextScene = mainMenu;

        Game *game = Game::getInstance();
        game->setGameOptions(_options);
    }
}

void OptionsMenu::onCancel(BOOL clicked)
{
    if (clicked)
    {
        setMenuFocus(_btnCancel);
        MainMenu *mainMenu = new MainMenu();
        mainMenu->setResolution(Size(512, 640));
        nextScene = mainMenu;
    }
}

///////////////////////////////////////////////////////////////////////////////
// Private methods
///////////////////////////////////////////////////////////////////////////////

void OptionsMenu::createObjects(void) throw (...)
{
    // TODO : order IS VERY IMPORTANT, so objects must have a priority in order
    // to make sure that methods like initialize, update, render ... are called
    // correctly

    Game *game = Game::getInstance();
    GameOptions initOptions = game->gameOptions();

	///////////////////////////////////////////////////////////////////////////
    // Create the DIFFICULTY label
    ///////////////////////////////////////////////////////////////////////////
    GameObject *label1 = new GameObject(this, "DifficultyLabel", "Label", HUD_LEVEL);
    // Create its components
    Transformation *transf1 = new Transformation(label1, 25.0, 590.0, 
        0.0, 0.0, 1.0, 1.0);
    SimpleText *text1 = new SimpleText(label1, TIMES_ROMAN_24, 
        ColorRGB(255.0, 255.0, 255.0));
    text1->setText("DIFFICULTY");
    // Add components to object
    label1->addComponent(transf1);
    label1->addComponent(text1);
    
	///////////////////////////////////////////////////////////////////////////
    // Create the DIFFICULTY options
    ///////////////////////////////////////////////////////////////////////////
    _btnEasy = new PushButton(this, "EasyButton", "Button", HUD_LEVEL);
    _btnEasy->setText("Easy");
    _btnEasy->setFont(TIMES_ROMAN_24);
    _btnEasy->setPosition(Point(100.0f, 550.0f));
    _btnEasy->setSize(Size(45.0f, 20.0f));
    if (initOptions.difficulty() == EASY)
    {
        _btnEasy->setFocus(true);
    }

    _btnMedium = new PushButton(this, "MediumButton", "Button", HUD_LEVEL);
    _btnMedium->setText("Medium");
    _btnMedium->setFont(TIMES_ROMAN_24);
    _btnMedium->setPosition(Point(215.0f, 550.0f));
    _btnMedium->setSize(Size(85.0f, 20.0f));
    if (initOptions.difficulty() == MEDIUM)
    {
        _btnMedium->setFocus(true);
    }

    _btnHard = new PushButton(this, "HardButton", "Button", HUD_LEVEL);
    _btnHard->setText("Hard");
    _btnHard->setFont(TIMES_ROMAN_24);
    _btnHard->setPosition(Point(365.0f, 550.0f));
    _btnHard->setSize(Size(50.0f, 20.0f));
    if (initOptions.difficulty() == HARD)
    {
        _btnHard->setFocus(true);
    }

    _difficultyGroup.push_back(_btnEasy);
    _difficultyGroup.push_back(_btnMedium);
    _difficultyGroup.push_back(_btnHard);

	///////////////////////////////////////////////////////////////////////////
    // Create the GRAPHIC DETAIL label
    ///////////////////////////////////////////////////////////////////////////
    GameObject *label2 = new GameObject(this, "DetailLabel", "Label", HUD_LEVEL);
    // Create its components
    Transformation *transf2 = new Transformation(label2, 25.0, 480.0, 
        0.0, 0.0, 1.0, 1.0);
    SimpleText *text2 = new SimpleText(label2, TIMES_ROMAN_24, 
        ColorRGB(255.0, 255.0, 255.0));
    text2->setText("GRAPHIC DETAIL");
    // Add components to object
    label2->addComponent(transf2);
    label2->addComponent(text2);
    
	///////////////////////////////////////////////////////////////////////////
    // Create the GRAPHIC DETAIL options
    ///////////////////////////////////////////////////////////////////////////
    _btnLow = new PushButton(this, "LowButton", "Button", HUD_LEVEL);
    _btnLow->setText("Low");
    _btnLow->setFont(TIMES_ROMAN_24);
    _btnLow->setPosition(Point(100.0f, 440.0f));
    _btnLow->setSize(Size(35.0f, 20.0f));
    if (initOptions.graphicDetail() == LOW)
    {
        _btnLow->setFocus(true);
    }

    _btnModerate = new PushButton(this, "ModerateButton", "Button", HUD_LEVEL);
    _btnModerate->setText("Medium");
    _btnModerate->setFont(TIMES_ROMAN_24);
    _btnModerate->setPosition(Point(215.0f, 440.0f));
    _btnModerate->setSize(Size(85.0f, 20.0f));
    if (initOptions.graphicDetail() == MODERATE)
    {
        _btnModerate->setFocus(true);
    }

    _btnHigh = new PushButton(this, "HighButton", "Button", HUD_LEVEL);
    _btnHigh->setText("High");
    _btnHigh->setFont(TIMES_ROMAN_24);
    _btnHigh->setPosition(Point(365.0f, 440.0f));
    _btnHigh->setSize(Size(50.0f, 20.0f));
    if (initOptions.graphicDetail() == HIGH)
    {
        _btnHigh->setFocus(true);
    }

    _graphDetailGroup.push_back(_btnLow);
    _graphDetailGroup.push_back(_btnModerate);
    _graphDetailGroup.push_back(_btnHigh);

	///////////////////////////////////////////////////////////////////////////
    // Create the MUSIC label
    ///////////////////////////////////////////////////////////////////////////
    GameObject *label3 = new GameObject(this, "MusicLabel", "Label", HUD_LEVEL);
    // Create its components
    Transformation *transf3 = new Transformation(label3, 25.0, 370.0, 
        0.0, 0.0, 1.0, 1.0);
    SimpleText *text3 = new SimpleText(label3, TIMES_ROMAN_24, 
        ColorRGB(255.0, 255.0, 255.0));
    text3->setText("MUSIC AND EFFECTS");
    // Add components to object
    label3->addComponent(transf3);
    label3->addComponent(text3);

	///////////////////////////////////////////////////////////////////////////
    // Create the MUSIC options
    ///////////////////////////////////////////////////////////////////////////
    _chkMusic = new CheckBox(this, "CheckMusic", "CheckBox", HUD_LEVEL);
    _chkMusic->setText("   Enable music");
    _chkMusic->setFont(TIMES_ROMAN_24);
    _chkMusic->setPosition(Point(75.0f, 310.0f));
    _chkMusic->setSize(Size(10.0f, 10.0f));
    _chkMusic->setChecked(initOptions.isMusicEnabled());

    _chkFX = new CheckBox(this, "CheckFx", "CheckBox", HUD_LEVEL);
    _chkFX->setText("   Enable FX");
    _chkFX->setFont(TIMES_ROMAN_24);
    _chkFX->setPosition(Point(310.0f, 310.0f));
    _chkFX->setSize(Size(10.0f, 10.0f));
    _chkFX->setChecked(initOptions.isFxEnabled());


    _btnOk = new PushButton(this, "OkButton", "Button", HUD_LEVEL);
    _btnOk->setText("OK");
    _btnOk->setFont(TIMES_ROMAN_24);
    _btnOk->setPosition(Point(310.0f, 50.0f));
    _btnOk->setSize(Size(40.0f, 20.0f));
    _btnOk->setDefault(true);

    _btnCancel = new PushButton(this, "CancelButton", "Button", HUD_LEVEL);
    _btnCancel->setText("CANCEL");
    _btnCancel->setFont(TIMES_ROMAN_24);
    _btnCancel->setPosition(Point(370.0f, 50.0f));
    _btnCancel->setSize(Size(100.0f, 20.0f));

    // Add objects to scene
    addObject(label1);
    addObject(_btnEasy);
    addObject(_btnMedium);
    addObject(_btnHard);

    addObject(label2);
    addObject(_btnLow);
    addObject(_btnModerate);
    addObject(_btnHigh);

    addObject(label3);
    addObject(_chkMusic);
    addObject(_chkFX);

    addObject(_btnOk);
    addObject(_btnCancel);

    _widgets.push_back(_btnOk);
    _widgets.push_back(_btnCancel);
}

void OptionsMenu::setDifficulty(Widget *widget)
{
    vector<Widget*>::const_iterator it;
    for (it=_difficultyGroup.begin(); it<_difficultyGroup.end(); it++)
    {
        if (widget == (*it))
        {
            (*it)->setFocus(true);
        }
        else
        {
            (*it)->setFocus(false);
        }
    }
}

void OptionsMenu::setGraphicDetail(Widget *widget)
{
    vector<Widget*>::const_iterator it;
    for (it=_graphDetailGroup.begin(); it<_graphDetailGroup.end(); it++)
    {
        if (widget == (*it))
        {
            (*it)->setFocus(true);
        }
        else
        {
            (*it)->setFocus(false);
        }
    }
}

void OptionsMenu::setMenuFocus(Widget *widget)
{
    vector<Widget*>::const_iterator it;
    for (it=_widgets.begin(); it<_widgets.end(); it++)
    {
        if (widget == (*it))
        {
            (*it)->setFocus(true);
        }
        else
        {
            (*it)->setFocus(false);
        }
    }
}
